package com.chen.gui.component.button;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.geom.GeneralPath;

import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.MenuElement;
import javax.swing.event.MouseInputAdapter;

import com.chen.Shell;
import com.chen.qq.Context;

public class PopupButton extends JComponent {

    private static final long serialVersionUID = -8282903550305637795L;

    private Icon icon;

    private int textIconGap = 0;

    private boolean isRollovered;

    private boolean pressed;

    private int arrowWidth = 15;

    private JPopupMenu popupMenu;

    private Handler handler = new Handler();

    public static void main(String[] args) {
        JPanel panel = new JPanel();
        panel.add(Context.getPopupButton("add.png"));
        Shell.run(panel);
    }

    public PopupButton() {
        this(null);
    }

    public PopupButton(Icon icon) {
        this.icon = icon;
        this.addMouseListener(handler);
        this.addMouseMotionListener(handler);
        this.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        this.setOpaque(false);
    }

    public JMenuItem add(Action action) {
        JMenuItem mi = new JMenuItem(action);
        mi.setAction(action);
        getPopupMenu().add(mi);
        return mi;
    }

    public JMenuItem add(JMenuItem mi) {
        return getPopupMenu().add(mi);
    }

    public void addGroupAction(Action... actions) {
        if (actions == null || actions.length == 0)
            return;

        if (actions.length == 1) {
            add(actions[0]);
            return;
        }

        ButtonGroup group = new ButtonGroup();
        for (Action action : actions) {
            JCheckBoxMenuItem item = new JCheckBoxMenuItem(action);
            group.add(item);
            add(item);
        }
    }

    public void setSelectedItem(String text) {
        JPopupMenu popup = getPopupMenu();
        for (int i = 0; i < popup.getComponentCount(); i++) {
            JMenuItem item = (JMenuItem) popup.getComponent(i);
            if (item.getText().equals(text)) {
                item.setSelected(true);
                item.doClick();
                break;
            }
        }
    }

    public void removeAllMenuItems() {
        getPopupMenu().removeAll();
    }

    public void addActionListener(ActionListener l) {
        listenerList.add(ActionListener.class, l);
    }

    public JPopupMenu getPopupMenu() {
        if (popupMenu == null) {
            popupMenu = new JPopupMenu();
            this.setComponentPopupMenu(popupMenu);
        }
        return popupMenu;
    }

    protected void fireActionPerformed(ActionEvent event) {
        Object[] listeners = listenerList.getListenerList();
        ActionEvent e = null;
        for (int i = listeners.length - 2; i >= 0; i -= 2) {
            if (listeners[i] == ActionListener.class) {
                if (e == null) {
                    String actionCommand = event.getActionCommand();
                    if (actionCommand == null) {
                        actionCommand = "";
                    }
                    e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand, event.getWhen(),
                            event.getModifiers());
                }
                ((ActionListener) listeners[i + 1]).actionPerformed(e);
            }
        }
    }

    void removeMenuItem(String... texts) {
        if (texts == null || texts.length == 0 || popupMenu == null)
            return;

        for (String text : texts)
            removeItem(popupMenu, text);
    }

    private void removeItem(MenuElement me, String text) {
        if (me == null)
            return;

        MenuElement[] items = me.getSubElements();
        if (items == null || items.length == 0) {
            if (me instanceof JMenuItem) {
                JMenuItem item = (JMenuItem) me;
                if (item.getText().equals(text))
                    popupMenu.remove(item);
            }
        } else {
            for (MenuElement elem : items)
                removeItem(elem, text);
        }
    }

    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g.create();

        if (popupMenu != null && popupMenu.isShowing())
            return;

        int w = getWidth();
        int h = getHeight();

        if (pressed) {
            g2d.translate(1, 1);
        } else if (isRollovered) {
            g2d.setPaint(new GradientPaint(0, 0, Context.borderColor, 0, h / 2, Context.backColor));
            g2d.fillRect(2, 2, w - 5, h - 5);
            g2d.setColor(Color.white);
            g2d.drawRect(2, 2, w - 5, h - 5);
        }

        Insets insets = getInsets();
        int x = insets.left;
        int y = insets.top;
        int width = w - insets.left - insets.right;
        int height = h - insets.top - insets.bottom;

        int total = icon.getIconWidth() + textIconGap + arrowWidth;
        x += (width - total) / 2;
        y += (height - icon.getIconHeight()) / 2;
        icon.paintIcon(this, g2d, x, y);

        x += icon.getIconWidth() + textIconGap;

        g2d.setColor(Color.white);
        int tx = x;
        int ty = insets.top;

        width -= x;
        g2d.translate(tx, ty);
        GeneralPath gp = new GeneralPath();
        gp.moveTo(arrowWidth / 3.0f, height * 0.40f);
        gp.lineTo(arrowWidth * 2 / 3f, height * 0.40f);
        gp.lineTo(arrowWidth / 2, height * 0.7f);
        gp.closePath();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.fill(gp);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        g2d.translate(-tx, -ty);
        g2d.fill(gp);
        g2d.dispose();
    }

    private void setRollover(boolean rollover) {
        if (!isEnabled())
            return;

        this.isRollovered = rollover;
        repaint();
    }

    @Override
    public void setEnabled(boolean enabled) {
        isRollovered = false;
        super.setEnabled(enabled);
    }

    @Override
    public Dimension getPreferredSize() {
        Insets insets = getInsets();
        int w = icon.getIconWidth() + textIconGap + arrowWidth + insets.left + insets.right;
        int h = icon.getIconHeight() + insets.top + insets.bottom;
        return new Dimension(w, h);
    }

    public Icon getIcon() {
        return icon;
    }

    public void setIcon(Icon icon) {
        this.icon = icon;
    }

    public int getTextIconGap() {
        return textIconGap;
    }

    public void setTextIconGap(int textIconGap) {
        this.textIconGap = textIconGap;
    }

    class Handler extends MouseInputAdapter {

        @Override
        public void mouseExited(MouseEvent e) {
            if (!isEnabled())
                return;

            setRollover(false);
        }

        @Override
        public void mousePressed(MouseEvent e) {
            if (!isEnabled() || !contains(e.getPoint()))
                return;

            if (popupMenu != null && popupMenu.isShowing()) {
                pressed = false;
            } else if (popupMenu != null) {
                pressed = true;
                popupMenu.show(PopupButton.this, getX(), getY() + getHeight());
            } else {
                pressed = true;
            }
            repaint();
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            pressed = false;
            repaint();
            fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ""));
        }

        @Override
        public void mouseMoved(MouseEvent e) {
            if (!isEnabled())
                return;

            Point point = e.getPoint();
            setRollover(contains(point));
        }
    }
}